﻿using System;
using System.IO;

namespace Game.Global.Helper
{
    /// <summary>
    /// 简单地加密文件，或者加密字符串为byte[]，以及相应地解密的辅助类
    /// </summary>
    public class SimpleDEncrypterHelper
    {
        enum ProcessWay
        {
            /// <summary>
            /// 加密
            /// </summary>
            Encrypt = 1,

            /// <summary>
            /// 解密
            /// </summary>
            Decrypt = 2,

        };

        const string C_ENCRYPT_SIGN = ".enc";
        #region Public part

        /// <summary>
        /// 加密文件
        /// </summary>
        /// <param name="pathInput"></param>
        /// <param name="pathOutput"></param>
        /// <param name="password"></param>
        public static void EncryptFile(string pathInput, string pathOutput, string password)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 解密文件
        /// </summary>
        /// <param name="pathInput"></param>
        /// <param name="pathOutput"></param>
        /// <param name="password"></param>
        public static void DecryptFile(string pathInput, string pathOutput, string password)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// 对string进行加密为对应的byte[]
        /// </summary>
        /// <param name="str"></param>
        /// <param name="password"></param>
        public static byte[] EncryptString(string str, string password)
        {
            return EncodeStringToBytes(str, password);
        }

        /// <summary>
        /// 对String进行加密为bytes，并且将bytes逐一转为string。
        /// <para>PS:【此方法会使得加密内容冗余，且速度会比直接加密为bytes的方法慢】</para>
        /// </summary>
        /// <param name="str">String.</param>
        /// <param name="password">Password.</param>
        /// <param name="bytesString">Byetes string.</param>
        public static void EncryptString(string str, string password, out string bytesString)
        {
            byte[] bytes = EncryptString(str, password);
            var sb = new System.Text.StringBuilder();
            foreach (var b in bytes)
            {
                sb.Append(b.ToString("X2"));
            }

            bytesString = sb.ToString();
        }

        /// <summary>
        /// 对string，从byte[]中进行解密
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="password"></param>
        public static string Decrypt(byte[] bytes, string password)
        {
            return DecodeBytesToString(bytes, password);
        }


        /// <summary>
        /// 将byte[]的冗余字符串表示，解密为真正的String
        /// <para>PS：【此方法会比直接解密bytes更慢】</para>
        /// </summary>
        /// <param name="bytesStr">bytes在string中的表示（每个byte占2位）</param>
        /// <param name="password"></param>
        public static string Decrypt(string bytesStr, string password)
        {
            
            if (bytesStr.Length % 2 != 0)
            {
                //非偶数倍的内容，证明不是byte[]的冗余表示，直接返回
                UnityEngine.Debug.LogError("Error, the string is not in bytes formation:" + bytesStr);
                return null;
            }
            int bytesCount = bytesStr.Length / 2;
            byte[] bytes = new byte[bytesCount];
            byte b;
            for (int i = 0; i < bytesCount;i++)
            {
                b =(byte) int.Parse(bytesStr.Substring(i * 2, 2),System.Globalization.NumberStyles.HexNumber);
                bytes[i] = b;
            }
            return Decrypt(bytes, password);
        }

        #endregion

        #region Private part:

        /// <summary>
        /// 根据输入的密码，开始处理文件
        /// </summary>
        private static void ProcessFile(ProcessWay m_processWay, string pathInput, string pathOutput, string password)
        {
            #region 获得输出文件名：
            //--处理输出路径
            switch (m_processWay)
            {
                case ProcessWay.Encrypt:
                    //是要加密，则输出文件会加上后缀名
                    pathOutput = pathInput + C_ENCRYPT_SIGN;

                    break;

                case ProcessWay.Decrypt:
                    //解密，将去掉后缀名
                    //--输入的文件名如果不符合规范
                    if (pathInput.Contains(C_ENCRYPT_SIGN))
                    {
                        char subChar = '.';
                        //获得输出的路径：
                        string outputPath = pathInput.Substring(0, pathInput.LastIndexOf(subChar));

                        pathOutput = outputPath;
                    }
                    else
                    {
                        UnityEngine.Debug.Log("Seem like that this file is not a encrpyted file.");
                        pathOutput = pathInput + ".DEC";
                    }



                    break;


            }

            UnityEngine.Debug.Log("out:\t" + pathOutput);

            #endregion

            //打开输入文件：
            StreamReader srR = new StreamReader(pathInput);
            BinaryReader fileReader = new BinaryReader(srR.BaseStream);

            //创建输出文件：
            StreamWriter srW = new StreamWriter(pathOutput);
            BinaryWriter fileWriter = new BinaryWriter(srW.BaseStream);

            const int BUFFER_SIZE = 10 * 1024 * 1024;
            //对拷
            byte[] buffer = new byte[BUFFER_SIZE];           //buffer
            int readCount;                                      //实际读取到长度
            while (true)
            {
                //读入buffer
                readCount = fileReader.Read(buffer, 0, BUFFER_SIZE);
                if (readCount <= 0)
                    break;

                //处理buffer内容
                ProcessBytes(buffer, readCount, m_processWay, password);

                //将Buffer内的内容写出：
                fileWriter.Write(buffer, 0, readCount);

            }

            srR.Close();
            srW.Close();

            UnityEngine.Debug.Log("Process Done.");

        }
        private static byte[] EncodeStringToBytes(string str, string password)
        {
            var utf8Encoder = new System.Text.UTF8Encoding();
            byte[] bytes = utf8Encoder.GetBytes(str);
            ProcessBytes(bytes, bytes.Length, ProcessWay.Encrypt, password);
            return bytes;
        }

        private static string DecodeBytesToString(byte[] bytes, string password)
        {
            ProcessBytes(bytes, bytes.Length, ProcessWay.Decrypt, password);
            return System.Text.Encoding.UTF8.GetString(bytes);
        }

        private static void ProcessBytes(byte[] buffer, int readCount, ProcessWay processWay, string password)
        {
            //生成随机数产生器
            int randGensCount = password.Length;              //--随机数生成器个数
            Random[] randGens = new Random[randGensCount];
            //--随机数初始化
            #region 随机数初始化
            for (int i = 0; i < randGensCount; i++)
            {
                randGens[i] = new Random(password[i]);
            }
            #endregion


            int currRander = 0;                                                  //--当前使用的随机数生成器
            int mulFactor =
                (processWay == ProcessWay.Encrypt ? 1 : -1);    //--将随机数加还是减的系数

            //对buffer进行运算操作
            for (int i = 0; i < readCount; i++)
            {
                buffer[i] = (byte)(buffer[i] + randGens[currRander].Next(256) * mulFactor);
                currRander = (currRander + 1) % randGensCount;
            }
        }


        //---------------------------------------------------------------------------------------------
        //                                                  常用输入输出函数
        //---------------------------------------------------------------------------------------------

        #endregion
    }
}